#include "game.h"
#include "base.h"
#include "text.h"
#include "setting.h"

using namespace std;
using namespace Ogre;

int get_terrain_size()
{
	Image tmp_image;
	tmp_image.load(TERRAIN_FILE_NAME, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);

	if (tmp_image.getHeight() != tmp_image.getWidth())
		throw Exception(7, "<terrain.png> need to be square", "game.cpp, get_terrain_size");

	return tmp_image.getHeight();
}

bool Base::load_world()
{
	if (terrain_group != NULL)
		return true;
	Light* l = scene_manager->createLight("sun");
	l->setCastShadows(true);
	l->setType(Light::LT_DIRECTIONAL);
	l->setSpecularColour(Ogre::ColourValue(1.f, 1.f, 1.f));
	l->setDiffuseColour(Ogre::ColourValue(0.7f, 0.7f, 0.7f));
	scene_manager->setAmbientLight(ColourValue(0.1f, 0.1f, 0.1f));
	l->setDirection(1.f, -1.f, 1.f);// preset

	terrain_option = new TerrainGlobalOptions();
	int size = get_terrain_size();
	terrain_group = new TerrainGroup(scene_manager, Terrain::ALIGN_X_Z, size, WORLD_SIZE);
	
	terrain_group->setFilenameConvention(FILE_TERRAIN, FILE_TERRAIN_EXT);
	

	terrain_group->setOrigin(Vector3(0.f, MIN_LEVEL, 0.f));

	terrain_option->setMaxPixelError(MAX_PIXEL_ERROR);
	terrain_option->setCompositeMapDistance(WORLD_SIZE);// TODO !! I don't know the difference
	
// Important to set these so that the terrain knows what to use for derived (non-realtime) data
	terrain_option->setLightMapDirection(l->getDerivedDirection());
	terrain_option->setCompositeMapAmbient(scene_manager->getAmbientLight());
	terrain_option->setCompositeMapDiffuse(l->getDiffuseColour());
	
// Configure default import settings for if we use imported image
	Ogre::Terrain::ImportData& defaultimp = terrain_group->getDefaultImportSettings();
	defaultimp.terrainSize = size; // decided by image. should be 2^k+1
	defaultimp.worldSize = terrain_group->getTerrainWorldSize();
	defaultimp.inputScale = (MAX_LEVEL - MIN_LEVEL); // due terrain.png is 8 bpp  TODO !! I don't know the difference

	// sample figure.. should be 2^k+1
	defaultimp.minBatchSize = 33;
	defaultimp.maxBatchSize = 65;

	defaultimp.layerList.resize(3);
	defaultimp.layerList[0].worldSize = 600;
	defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_diffusespecular.dds");
	defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_normalheight.dds");
	defaultimp.layerList[1].worldSize = 600;
	defaultimp.layerList[1].textureNames.push_back("grass_green-01_diffusespecular.dds");
	defaultimp.layerList[1].textureNames.push_back("grass_green-01_normalheight.dds");
	defaultimp.layerList[2].worldSize = 600;
	defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");
	defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");
	
	terrain_group->freeTemporaryResources();

	Ogre::String filename = terrain_group->generateFilename(0, 0);
	if (Ogre::ResourceGroupManager::getSingleton().resourceExists(terrain_group->getResourceGroup(), filename))
	{
		terrain_group->defineTerrain(0, 0);
	}
	else
	{
		first_run = true;
		Ogre::Image img;
		img.load(TERRAIN_FILE_NAME, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		terrain_group->defineTerrain(0, 0, &img);
	}
	
	terrain_group->loadAllTerrains(true);
	Ogre::TerrainGroup::TerrainIterator ti = terrain_group->getTerrainIterator();
	while(ti.hasMoreElements())
	{
		Ogre::Terrain* t = ti.getNext()->instance;
		Ogre::TerrainLayerBlendMap* blendMap0 = t->getLayerBlendMap(1);
		Ogre::TerrainLayerBlendMap* blendMap1 = t->getLayerBlendMap(2);
		Ogre::Real minHeight0 = GRASS_LEVEL;
		Ogre::Real fadeDist0 = FADE_DIST_1;
		Ogre::Real minHeight1 = ROCK_LEVEL;
		Ogre::Real fadeDist1 = FADE_DIST_2;
		float* pBlend0 = blendMap0->getBlendPointer();
		float* pBlend1 = blendMap1->getBlendPointer();
		for (Ogre::uint16 y = 0; y < t->getLayerBlendMapSize(); ++y)
			for (Ogre::uint16 x = 0; x < t->getLayerBlendMapSize(); ++x)
			{
				Ogre::Real tx, ty;
				blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
				Ogre::Real height = t->getHeightAtTerrainPosition(tx, ty);
				Ogre::Real val = (height - minHeight0) / fadeDist0;
				val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
				*pBlend0++ = val;

				val = (height - minHeight1) / fadeDist1;
				val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
				*pBlend1++ = val;
			}
		blendMap0->dirty();
		blendMap1->dirty();
		blendMap0->update();
		blendMap1->update();
	}
	
	terrain_group->freeTemporaryResources();
	
	terrain_option->setCastsDynamicShadows(true);
	

	
	Ogre::Plane oceanSurface;

	oceanSurface.normal = Ogre::Vector3::UNIT_Y;
    oceanSurface.d = SEA_LEVEL;
	
    Ogre::MeshManager::getSingleton().createPlane("OceanSurface",
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        oceanSurface,
        WORLD_SIZE * 3.5, WORLD_SIZE * 3.5, 10, 10, true, 1, 1, 1, Ogre::Vector3::UNIT_Z);
	
	Ogre::Entity* ent = scene_manager->createEntity("OceanSurface", "OceanSurface");
	ent->setMaterialName("Ocean2_Cg");
    scene_manager->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
	
	
	Ogre::Plane plane;
	plane.d = SKY_LEVEL;
	plane.normal = Ogre::Vector3::NEGATIVE_UNIT_Y;
	
	scene_manager->setSkyPlane(true, plane, "CloudySky", 3000, 10, true, 0.2f, 20, 20);

	Ogre::ColourValue fadeColour(0.8f, 0.75f, 0.8f);
	window->getViewport(0)->setBackgroundColour(fadeColour);
	scene_manager->setFog(Ogre::FOG_LINEAR, fadeColour, 0.0, WORLD_SIZE / 3, WORLD_SIZE);

	cam->setPosition(WORLD_SIZE / 2,MAX_LEVEL/2,WORLD_SIZE / 2);
	cam->setDirection(-1,0.f, -1);
	cam->setFarClipDistance(WORLD_SIZE);
	if (root->getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_INFINITE_FAR_PLANE))
		cam->setFarClipDistance(0);   // enable infinite far clip distance if we can
	cam->setNearClipDistance(10.f);
	
	Entity* g = scene_manager->createEntity("fighter", FIGHTER_FILE_NAME);
	
	SceneNode* f = scene_manager->getRootSceneNode()->createChildSceneNode("node_fighter");
	SceneNode* ft = f->createChildSceneNode("node_fighter_inside");

	Node* pl = f->createChild("node_fighter_left", Vector3(-17, -5, 30));
	Node* pr = f->createChild("node_fighter_right", Vector3(17, -5, 30));

	NameValuePairList params;
	params["numberOfChains"] = "2";
	params["maxElements"] = "80";

	RibbonTrail* rt = 
		(RibbonTrail*)scene_manager->createMovableObject("left_trial", "RibbonTrail", &params);
	
	RibbonTrail* rt2 = 
		(RibbonTrail*)scene_manager->createMovableObject("right_trial", "RibbonTrail", &params);

	scene_manager->getRootSceneNode()->attachObject(rt);
	scene_manager->getRootSceneNode()->attachObject(rt2);

	rt->setMaterialName("Examples/LightRibbonTrail");
	rt->setTrailLength(4000);
	rt->setRenderQueueGroup(55);

	rt->setInitialColour(0, 1.0f, 0.4f, 0.2f);
	rt->setColourChange(0, 5.f, 5.f, 5.f, 5.f);
	rt->setInitialWidth(0, 10);
	rt->addNode(pl);

	rt2->setMaterialName("Examples/LightRibbonTrail");
	rt2->setTrailLength(4000);
	rt2->setRenderQueueGroup(55);

	rt2->setInitialColour(0, 1.0f, 0.4f, 0.2f);
	rt2->setColourChange(0, 5.f, 5.f, 5.f, 5.f);
	rt2->setInitialWidth(0, 10);
	rt2->addNode(pr);

	ParticleSystem* thrusters = scene_manager->createParticleSystem(25);
	thrusters->setKeepParticlesInLocalSpace(true);
	thrusters->setMaterialName("Examples/Flare");
	thrusters->setDefaultDimensions(15, 15);

	// create two emitters for our thruster particle system
	for (unsigned int i = 0; i < 2; i++)
	{
		ParticleEmitter* emitter = thrusters->addEmitter("Point");  // add a point emitter
		// set the emitter properties
		emitter->setAngle(Degree(3));
		emitter->setTimeToLive(0.5);
		emitter->setEmissionRate(25);
		emitter->setParticleVelocity(25);
		emitter->setDirection(Vector3::UNIT_Z);
		emitter->setColour(ColourValue::White, ColourValue::Red);
		emitter->setPosition(Vector3(i == 0 ? -17 : 17, -5, 30));
	}

	// attach our thruster particles to the rear of the ship
	scene_manager->getSceneNode("node_fighter")->createChildSceneNode("node_flare")->attachObject(thrusters);


	ft->attachObject(g);
	ft->scale(5,5,5);
	ft->translate(0,-2,-5);
	//ft->yaw(Degree(-5.f));
	f->setPosition(WORLD_SIZE/2 - 300, MAX_LEVEL/2-200, WORLD_SIZE / 2 - 300);
	cam->lookAt(f->getPosition());
	return true;
}

void set_start_pos(SceneManager* sm)
{
	SceneNode* g = sm->getSceneNode("node_carrier");
	g->setPosition(Vector3(WORLD_SIZE/2 - 300, SEA_LEVEL + 310, WORLD_SIZE/2 - 300));
	sm->getSceneNode("node_fighter")->setPosition(g->getPosition()+Vector3(-420, 40, 2300));
}

static Vector3 target;

void place_target(SceneManager* sm, const Vector3& b)
{
	target = b;
	sm->getSceneNode("node_target")->setPosition(b);
}

bool hit_target(const Vector3& pos)
{
	return target.squaredDistance(pos) < TARGET_R * TARGET_R;
}

Vector3 pointer_target(Vector3 pos, const Quaternion& orientation)
{
	Vector3 tx, ty, tz;
	pos = target - pos;

	orientation.ToAxes(tx, ty, tz);
	pos = Vector3(tx.dotProduct(pos), ty.dotProduct(pos), tz.dotProduct(pos));
	pos.normalise();
	return pos;
}

static int score;

void reset_score()
{
	score = 0;
}

int show_score()
{
	return score;
}

void increase_score()
{
	score++;
}
